﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace SharePoint.Orm.Helpers {
	public static class TypeSystem {
		public static T Cast<T>(object obj) {
			if (object.Equals(obj, default(T)) || object.Equals(obj, null)) {
				return default(T);
			}

			if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)) {
				return (T)Convert.ChangeType(obj, typeof(T).GetGenericArguments()[0]);
			}

			return (T)Convert.ChangeType(obj, typeof(T));
		}

		public static Type GetElementType(Type seqType) {
			Type ienum = FindIEnumerable(seqType);
			if (ienum == null) return seqType;
			return ienum.GetGenericArguments()[0];
		}

		private static Type FindIEnumerable(Type seqType) {
			if (seqType == null || seqType == typeof(string))
				return null;

			if (seqType.IsArray)
				return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());

			if (seqType.IsGenericType) {
				foreach (Type arg in seqType.GetGenericArguments()) {
					Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
					if (ienum.IsAssignableFrom(seqType)) {
						return ienum;
					}
				}
			}

			Type[] ifaces = seqType.GetInterfaces();
			if (ifaces != null && ifaces.Length > 0) {
				foreach (Type iface in ifaces) {
					Type ienum = FindIEnumerable(iface);
					if (ienum != null) return ienum;
				}
			}

			if (seqType.BaseType != null && seqType.BaseType != typeof(object)) {
				return FindIEnumerable(seqType.BaseType);
			}

			return null;
		}

		public static string TypeToCodeString(Type type) {
			if (type == typeof(int)) {
				return "int";
			}
			if (type == typeof(decimal)) {
				return "decimal";
			}
			if (type == typeof(string)) {
				return "string";
			}
			if (type == typeof(bool)) {
				return "bool";
			}
			if (type == typeof(object)) {
				return "object";
			}
			if (type.IsGenericType) {
				if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
					return TypeToCodeString(type.GetGenericArguments()[0]) + "?";
				}
				string name = type.FullName;
				int lastTick = name.IndexOf('`');
				name = name.Replace('+', '.');
				name = name.Substring(0, lastTick);
				return name + "<" + string.Join(", ", type.GetGenericArguments().Select(t => TypeToCodeString(t)).ToArray()) + ">";
			}
			return type.FullName;
		}
	}
}